#-*- coding:Utf-8 -*-

from networkx import *
from donnees import *
from random import *
from distance import *
from time import time

""" fonction ---> Ce module contient des méthodes utiles: Voisin, Comparer_Des_Decision, Energie_Capteur, Energie_Agregateur, Genere_Data, Quelle_Categorie, Extrait_Echantillon, Fils_De, Arbre_Donne_Etat """


def Voisin(puits, capteur) :

    """ Voisin(puits, capteur) ---> return les voisins de capteur """

    orde = sorted(puits.dicoCapteurs.values(), key=lambda k: sqrt((k.x-capteur.x)**2 + (k.y-capteur.y)**2))   
    ordre = [C for C in orde if  sqrt((C.x-capteur.x)**2 + (C.y-capteur.y)**2) <= Range ]

    return ordre

def Comparer_Des_Decision(reel, foret):

    """ Comparer_Des_Decision(reel, foret) ---> return un nombre entier qui indique le nombre de decision égaux dans les deux lists reel et foret   """

    i = 0
    for k in range(len(reel)):
        if reel[k] == foret[k]:
            i += 1
    return i 

    


def Energie_Capteur(capteur, cible, indice, temps = 0):

    """ Energie_Capteur(tempscourant=None, temps=None, capteur, agregateurCible, indice) ---> cette méthode permet de décrémenter l’énergie d'un capteur à chaque réception des données ou envoie à l'agrégateur, afin de vérifier si le capteur est toujours disponible """

    # indice = 1 si le capteur capter le donnée et l'envoie a l'agregateur ou capteur cible si Topologie = 2
    # indice = 2 si le capteur envoie les données au capteur cible (dans cas si Topologie = 2)

    if indice == 1:
        return eval(Perdu_Energie_Capteur1)

    elif indice == 2:
        return eval(Perdu_Energie_Capteur2)


def Energie_Agregateur(agregateur, agregateurCible, indice, temps = 0):

   """ Energie_Agregateur(tempscourant=None, temps=None, agregateur, agregateurCible, indice) ---> cette méthode permet de décrémenter l’énergie d'un agrégateur à chaque  agrégation des données ou envoie à l’agrégateur cible, afin de vérifier si l’agrégateur est toujours disponible  """ 
 
   # indice = 1 : si l'agregateur agreger les données apres anvoyer a l'agregateur cible
   # indice = 2 : si l'agregateur va envoyer les données a l'agregateur cible

   if indice == 1:
       return eval(Perdu_Energie_Agregateur1) 

   elif indice == 2 :  
       return eval(Perdu_Energie_Agregateur2)   
           

def Genere_Data(lignes):

    """ Genere_Data(nb_lignes) ---> retourne des données aléatoires de n lignes """

    global heure
    L = []
    for k in range(lignes):
        L.append({})
        for l in Categories.keys():
            L[k][l] = []
            for m in range(len(Categories[l]['capteursDetectentPanne'])):
                if not Categories[l]['capteursDetectentPanne'][m]:
                    L[k][l].append(eval(Categories[l]['loi']))
                    if eval(loiPanne) >= 1:
                        Categories[l]['capteursDetectentPanne'][m] = True
                          
                else:
                    L[k][l].append(eval(Categories[l]['loiPanne']))     
        heure += 100./individus               
                       
    return L


def Quelle_Categorie(ligne, caractere = ''):

    """ Quelle_Categorie(ligne, caractere = '') ---> retourne la catégorie d'une ligne de données """

    if caractere == '':
        categ = Categories.keys()
    else:
        categ = [caractere]
    premier = True
    for l in categ:
        premier &= (max(ligne[l]) < Categories[l]['limites'][0])
    if premier:
        return 0
    for k in range(1,nbCategories):
       cate = True       
       for l in categ:
           cate &= max(ligne[l]) <= Categories[l]['limites'][k-1]
       if cate:
           return k-1
    return nbCategories-1


def Extrait_Echantillon(population, proportion = 67):

    """ Extrait_Echantillon(population, proportion = 67) ---> retourne une échantillon de données (67 % par défaut) """

    L = []
    cpt = 0
    while (float(cpt) / len(population))*100 < proportion:
        individu = choice(population)
        if individu not in L:
            cpt += 1
           
        L.append(individu)
    return L



def Fils_De(echantillon, G, L, root):

    """ Fils_De(echantillon, G, L, root) ---> retourne la root et ses catégories de cette echantillon """

    categor = Categories.keys()
    if len(G.edges()) > 0:
        liste = shortest_path(G,root,L)
        for k in range(len(liste)-1):
            cat = G.edge[liste[k]][liste[k+1]]['label'].split('_')[0]
            if cat in categor:
                categor.remove(cat)
        if categor == []:
            return None
    resultats = {}
    nouveauxEchantillons = {}
    for categorie in categor:
        resultats[categorie] = []
        nouveauxEchantillons[categorie] = []
        for k in range(nbCategories):
            resultats[categorie].append([])
            nouveauxEchantillons[categorie].append([])
        tot = sum(L)
        resultats[categorie].append(sum([-float(kk)/tot*log(float(kk)/tot,2) for kk in L if kk > 0]))

        for limite in range(nbCategories):
            resultats[categorie][limite] = [0]*(nbCategories+1)
            for individu in echantillon:
                if limite == 0:
                    if max(individu[categorie]) < Categories[categorie]['limites'][0]:
                        resultats[categorie][0][Quelle_Categorie(individu)] += 1
                        nouveauxEchantillons[categorie][0].append(individu)
                elif limite == nbCategories-1:
                    if max(individu[categorie]) > Categories[categorie]['limites'][-1]:
                        resultats[categorie][nbCategories-1][Quelle_Categorie(individu)] += 1                        
                        nouveauxEchantillons[categorie][nbCategories-1].append(individu)
                elif max(individu[categorie]) >= Categories[categorie]['limites'][limite-1] and max(individu[categorie]) < Categories[categorie]['limites'][limite]:
                  
                    resultats[categorie][limite][Quelle_Categorie(individu)] += 1
                    nouveauxEchantillons[categorie][limite].append(individu)

            tot = sum(resultats[categorie][limite][:-1])
            # Stockage de l'entropie
            if tot>0:
                resultats[categorie][limite][-1] = sum([-float(kk)/tot*log(float(kk)/tot,2) for kk in resultats[categorie][limite][:-1] if kk > 0])
                resultats[categorie][-1] -= float(tot)/sum(L)*resultats[categorie][limite][-1]
    aRetenir = max([(categorie,resultats[categorie][-1]) for categorie in categor], key=lambda x:x[1])[0]
    noeuds = []
    for elem, noeud in enumerate(resultats[aRetenir]):
        if isinstance(noeud, list):
            noeuds.append(tuple(noeud[:-1]))
    return aRetenir, noeuds, nouveauxEchantillons[aRetenir]


def Arbre_Donne_Etat(graphe, racine, aTester):

    """ Arbre_Donne_Etat(graphe, racine, Teste) ---> retourne une décision sur des données (la décision est entier qui peut être 0, 1, 2, 3 ou 4 ) """

    t = [] 
    noeud = racine

    while graphe.edges(noeud) != []:
        
        u,v = graphe.edges(noeud)[0]
        caracteristique = graphe.edge[u][v]['label'].split('_')[0]
        arete = caracteristique+'_'+str(Quelle_Categorie(aTester, caracteristique))
        
        test = [k for k in graphe.successors(noeud) if graphe.edge[noeud][k]['label']]
        #noeud=v
        if test == t:
            raise NameError("On ne trouve pas le chemin pour prendre la décision")

        else:
            noeud = [k for k in graphe.successors(noeud) if graphe.edge[noeud][k]['label'] == arete][0]

    if noeud in [-1,-2,-3,-4,-5] :
        return -1
    for k in range(len(noeud)):
        if noeud[k] != 0:
            return k

